package com.yb.guigu.pick.rabbitmq.messager.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.yb.guigu.pick.core.constant.RabbitMQConstant;
import com.yb.guigu.pick.core.domain.mq.BackupMQSend;
import com.yb.guigu.pick.rabbitmq.messager.api.model.MessageContent;
import com.yb.guigu.pick.rabbitmq.messager.api.model.RabbitMQMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.lang.NonNull;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

/**
 * Copyright (C), 2022-2023, 姚兵
 * Author: 32210
 * Date: 2023/5/9 14:09
 * FileName: RabbitMQMessageManager
 * Description:
 */
@Slf4j
public class GuiguPickRabbitMQMessager implements GuiguPickMessager,RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback, InitializingBean, ApplicationContextAware {

    RabbitTemplate rabbitTemplate;

    GuiguPickRabbitMQMessager guiguPickRabbitMQMessager;

    public GuiguPickRabbitMQMessager(RabbitTemplate rabbitTemplate){
        this.rabbitTemplate=rabbitTemplate;

    }

    @Override
    public <T>  void convertAndSend(RabbitMQMessage<?> message) {
        log.info("开始发送消息，消息id:{}",message.getRabbitId());

        String rabbitId = message.getRabbitId();
        if(!StringUtils.hasText(rabbitId)){
            String uuid = UUID.randomUUID().toString().replace("-", "");
            message.setRabbitId(uuid);
        }
        String body = JSONObject.toJSONString(message.getContent());
        rabbitTemplate.convertAndSend(message.getExchange(),message.getRoutingKey()
                ,message.getContent(),correlationData(message,body));
    }

    @Override
    @Async
    public <T> void convertAndSendAsync(RabbitMQMessage<?> message) {

        log.info("开始发送消息，消息id:{}",message.getRabbitId());

        if(!StringUtils.hasText(message.getRabbitId())){
            String uuid = UUID.randomUUID().toString().replace("-", "");
            message.setRabbitId(uuid);
        }

        String body = JSONObject.toJSONString(message.getContent());
        rabbitTemplate.convertAndSend(message.getExchange(),message.getRoutingKey()
                ,message.getContent(),correlationData(message,body));
    }

    private CorrelationData correlationData(RabbitMQMessage message,String body){

        CorrelationData correlationData = new CorrelationData();

        correlationData.setId(message.getRabbitId());

        Message mqMessage = new Message(body.getBytes(StandardCharsets.UTF_8));

        correlationData.setReturned(new ReturnedMessage(mqMessage,1,"success",message.getExchange(),message.getRoutingKey()));
        return correlationData;
    }
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {

        ReturnedMessage returned = correlationData.getReturned();
        byte[] body = returned.getMessage().getBody();

        //如果是备份的mq，则不进行处理
        if(returned.getExchange().equals(RabbitMQConstant.BackupMQQueueConstant.EXCHANGE)){
            return;
        }

        BackupMQSend backupMQSend = BackupMQSend.builder().ack(String.valueOf(ack))
                .message(new String(body, StandardCharsets.UTF_8))
                .msgId(correlationData.getId())
                .exchange(returned.getExchange())
                .routingKey(returned.getRoutingKey())
                .replyCode(returned.getReplyCode())
                .replyText(returned.getReplyText()).build();

        backupMQSend.setUuid(correlationData.getId());

        if (ack) {
            log.info("send mq message to exchange success {} ", correlationData);
        } else {
            log.error("send mq message to exchange failure {} --> {}", correlationData, cause);
            backupMQSend.setException(cause);
        }

        RabbitMQMessage<MessageContent<?>> rabbitMQMessage = RabbitMQMessage.builder().content(MessageContent.builder().content(backupMQSend).build()).build();
        rabbitMQMessage.setRabbitId("backup-"+correlationData.getId());
        rabbitMQMessage.setExchange(RabbitMQConstant.BackupMQQueueConstant.EXCHANGE);
        rabbitMQMessage.setRoutingKey(RabbitMQConstant.BackupMQQueueConstant.ROUTING_KEY);
        rabbitMQMessage.getContent().setMsgId(rabbitMQMessage.getRabbitId());



        guiguPickRabbitMQMessager.convertAndSendAsync(rabbitMQMessage);


    }

    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
        log.error("send message to queue fail. exchange {} with routingkey {}. reply code {} , text {} , message {} "
                , returnedMessage.getExchange(), returnedMessage.getRoutingKey(), returnedMessage.getReplyCode(), returnedMessage.getReplyText(), JSON.toJSONString(returnedMessage.getMessage()));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.rabbitTemplate.setReturnsCallback(this);
        this.rabbitTemplate.setConfirmCallback(this);
        this.guiguPickRabbitMQMessager=applicationContext.getBean(GuiguPickRabbitMQMessager.class);
    }

    ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}
